import java.util.ArrayList;
import java.util.List;

public class Matrice {
	private List<List<Integer>> matrice;
	private int ligne;
	private int colonne;
	
	public Matrice(int ligne, int colonne, List<int[]> piecePlace) {
		matrice = new ArrayList<List<Integer>>();
		this.ligne = ligne;
		this.colonne = colonne;
		
		for(int i=0; i<ligne*colonne; i++) {
			// Est-ce que la case est déjà occupée ?
			boolean caseLibre = true;
			for(int[] casePiece : piecePlace) {
				for(int c : casePiece) {
					if(c == i) {
						caseLibre = false;
					}
				}
			}
			// Si elle ne l'est pas, on crée les lignes dans la matrice
			if(caseLibre) {
				for(Forme f : Forme.values()) {
					for(Sens sens : Sens.values()) {
						List<Integer> lst = placeIsGood(f, i, sens);
						if(lst != null) {
							List<Integer> l = new ArrayList<Integer>();
							// Possibilité en fonction d'une case
							for(int j=0; j<ligne*colonne; j++) {
								if(lst.contains(j)) {
									l.add(1);
								} else {
									l.add(0);
								}
							}
							matrice.add(l);
						}
					}
				}
			}
		}
	}
	
	public List<List<Integer>> getMatrice() {
		return matrice;
	}
	
	
	/* Fonctions pour la création de la matrice */
	private List<Integer> placeIsGood(Forme forme, int num, Sens sens) {
		List<Integer> lst = new ArrayList<Integer>();
		switch(forme) {
		case F:
			lst = casesF(num, sens);
			break;
		case I:
			lst = casesI(num, sens);
			break;
		case L:
			lst = casesL(num, sens);
			break;
		case N:
			lst = casesN(num, sens);
			break;
		case P:
			lst = casesP(num, sens);
			break;
		case T:
			lst = casesT(num, sens);
			break;
		case U:
			lst = casesU(num, sens);
			break;
		case V:
			lst = casesV(num, sens);
			break;
		case W:
			lst = casesW(num, sens);
			break;
		case X:
			lst = casesX(num, sens);
			break;
		case Y:
			lst = casesY(num, sens);
			break;
		case Z:
			lst = casesZ(num, sens);
			break;
		}
		
		return lst;
	}

	private List<Integer> casesF(int num, Sens sens) {
		List<Integer> lst = new ArrayList<Integer>();
		List<Integer> argC = new ArrayList<Integer>();
		List<Integer> argL = new ArrayList<Integer>();

		lst.add(num);
		switch(sens) {
		case UP:
			lst.add(num-1);
			lst.add(num-colonne);
			lst.add(num+colonne);
			lst.add(num-colonne+1);
			break;
		case RIGHT:
			lst.add(num-1);
			lst.add(num-colonne);
			lst.add(num+1);
			lst.add(num+colonne+1);
			break;
		case DOWN:
			lst.add(num+1);
			lst.add(num-colonne);
			lst.add(num+colonne);
			lst.add(num+colonne-1);
			break;
		case LEFT:
			lst.add(num-1);
			lst.add(num+1);
			lst.add(num+colonne);
			lst.add(num-colonne-1);
			break;
		}
		
		argC.add(1);
		argC.add(-1);
		argL.add(1);
		argL.add(-1);
		
		if(!isInPlateau(num, argC, argL)) {
			return null;
		}
		
		return lst;
	}
	
	private List<Integer> casesI(int num, Sens sens) {
		List<Integer> lst = new ArrayList<Integer>();
		List<Integer> argC = new ArrayList<Integer>();
		List<Integer> argL = new ArrayList<Integer>();
		
		lst.add(num);
		switch(sens) {
		case UP:
			lst.add(num-1);
			lst.add(num+1);
			lst.add(num-2);
			lst.add(num+2);
			
			argC.add(2);
			argC.add(-2);
			break;
		case RIGHT:
			lst.add(num-colonne);
			lst.add(num+colonne);
			lst.add(num+2*colonne);
			lst.add(num-2*colonne);
			
			argL.add(2);
			argL.add(-2);
			break;
		case DOWN:
		case LEFT:
			return null;
		}
		
		if(!isInPlateau(num, argC, argL)) {
			return null;
		}
		
		return lst;
	}

	private List<Integer> casesL(int num, Sens sens) {
		List<Integer> lst = new ArrayList<Integer>();
		List<Integer> argC = new ArrayList<Integer>();
		List<Integer> argL = new ArrayList<Integer>();
		
		lst.add(num);
		switch(sens) {
		case UP:
			lst.add(num+1);
			lst.add(num+2);
			lst.add(num+3);
			lst.add(num+colonne);
			
			argC.add(3);
			argL.add(1);
			break;
		case RIGHT:
			lst.add(num+colonne);
			lst.add(num+2*colonne);
			lst.add(num+3*colonne);
			lst.add(num-1);
			
			argC.add(-1);
			argL.add(3);
			break;
		case DOWN:
			lst.add(num-1);
			lst.add(num-2);
			lst.add(num-3);
			lst.add(num-colonne);
			
			argC.add(-3);
			argL.add(-1);
			break;
		case LEFT:
			lst.add(num+1);
			lst.add(num-colonne);
			lst.add(num-2*colonne);
			lst.add(num-3*colonne);
			
			argC.add(1);
			argL.add(-3);
			break;
		}
		
		if(!isInPlateau(num, argC, argL)) {
			return null;
		}
		
		return lst;
	}

	private List<Integer> casesN(int num, Sens sens) {
		List<Integer> lst = new ArrayList<Integer>();
		List<Integer> argC = new ArrayList<Integer>();
		List<Integer> argL = new ArrayList<Integer>();
		
		lst.add(num);
		switch(sens) {
		case UP:
			lst.add(num-1);
			lst.add(num-2);
			lst.add(num+colonne);
			lst.add(num+colonne+1);
			
			argC.add(-2);
			argC.add(1);
			argL.add(1);
			break;
		case RIGHT:
			lst.add(num-colonne);
			lst.add(num-2*colonne);
			lst.add(num-1);
			lst.add(num+colonne-1);
			
			argC.add(-1);
			argL.add(-2);
			argL.add(1);
			break;
		case DOWN:
			lst.add(num-colonne);
			lst.add(num-colonne-1);
			lst.add(num+1);
			lst.add(num+2);
			
			argC.add(2);
			argC.add(-1);
			argL.add(-1);
			break;
		case LEFT:
			lst.add(num+colonne);
			lst.add(num+2*colonne);
			lst.add(num+1);
			lst.add(num-colonne+1);
			
			argC.add(1);
			argL.add(2);
			argL.add(-1);
			break;
		}
		
		if(!isInPlateau(num, argC, argL)) {
			return null;
		}
		
		return lst;
	}

	private List<Integer> casesP(int num, Sens sens) {
		List<Integer> lst = new ArrayList<Integer>();
		List<Integer> argC = new ArrayList<Integer>();
		List<Integer> argL = new ArrayList<Integer>();
		
		lst.add(num);
		switch(sens) {
		case UP:
			lst.add(num+1);
			lst.add(num+2);
			lst.add(num+colonne+1);
			lst.add(num+colonne+2);
			
			argC.add(2);
			argL.add(1);
			break;
		case RIGHT:
			lst.add(num+colonne);
			lst.add(num+2*colonne);
			lst.add(num+colonne-1);
			lst.add(num+2*colonne-1);
			
			argC.add(-1);
			argL.add(2);
			break;
		case DOWN:
			lst.add(num-1);
			lst.add(num-2);
			lst.add(num-colonne-1);
			lst.add(num-colonne-2);
			
			argC.add(-2);
			argL.add(-1);
			break;
		case LEFT:
			lst.add(num-colonne);
			lst.add(num-2*colonne);
			lst.add(num-colonne+1);
			lst.add(num-2*colonne+1);
			
			argC.add(1);
			argL.add(-2);
			break;
		}
		
		if(!isInPlateau(num, argC, argL)) {
			return null;
		}
		
		return lst;
	}

	private List<Integer> casesT(int num, Sens sens) {
		List<Integer> lst = new ArrayList<Integer>();
		List<Integer> argC = new ArrayList<Integer>();
		List<Integer> argL = new ArrayList<Integer>();
		
		lst.add(num);
		switch(sens) {
		case UP:
			lst.add(num-colonne);
			lst.add(num+1);
			lst.add(num+2);
			lst.add(num+colonne);
			
			argC.add(2);
			argL.add(1);
			argL.add(-1);
			break;
		case RIGHT:
			lst.add(num-1);
			lst.add(num+1);
			lst.add(num+colonne);
			lst.add(num+2*colonne);
			
			argC.add(1);
			argC.add(-1);
			argL.add(2);
			break;
		case DOWN:
			lst.add(num-1);
			lst.add(num-2);
			lst.add(num-colonne);
			lst.add(num+colonne);
			
			argC.add(-2);
			argL.add(1);
			argL.add(-1);
			break;
		case LEFT:
			lst.add(num-1);
			lst.add(num+1);
			lst.add(num-colonne);
			lst.add(num-2*colonne);
			
			argC.add(1);
			argC.add(-1);
			argL.add(-2);
			break;
		}
		
		if(!isInPlateau(num, argC, argL)) {
			return null;
		}
		
		return lst;
	}

	private List<Integer> casesU(int num, Sens sens) {
		List<Integer> lst = new ArrayList<Integer>();
		List<Integer> argC = new ArrayList<Integer>();
		List<Integer> argL = new ArrayList<Integer>();
		
		lst.add(num);
		switch(sens) {
		case UP:
			lst.add(num-1);
			lst.add(num+colonne-1);
			lst.add(num+1);
			lst.add(num+colonne+1);
			
			argC.add(1);
			argC.add(-1);
			argL.add(1);
			break;
		case RIGHT:
			lst.add(num-colonne);
			lst.add(num-colonne-1);
			lst.add(num+colonne);
			lst.add(num+colonne-1);
			
			argC.add(-1);
			argL.add(1);
			argL.add(-1);
			break;
		case DOWN:
			lst.add(num-1);
			lst.add(num+1);
			lst.add(num-colonne-1);
			lst.add(num-colonne+1);
			
			argC.add(1);
			argC.add(-1);
			argL.add(-1);
			break;
		case LEFT:
			lst.add(num-colonne);
			lst.add(num-colonne+1);
			lst.add(num+colonne);
			lst.add(num+colonne+1);
			
			argC.add(1);
			argL.add(1);
			argL.add(-1);
			break;
		}
		
		if(!isInPlateau(num, argC, argL)) {
			return null;
		}
		
		return lst;
	}

	private List<Integer> casesV(int num, Sens sens) {
		List<Integer> lst = new ArrayList<Integer>();
		List<Integer> argC = new ArrayList<Integer>();
		List<Integer> argL = new ArrayList<Integer>();
		
		lst.add(num);
		switch(sens) {
		case UP:
			lst.add(num+1);
			lst.add(num+2);
			lst.add(num+colonne);
			lst.add(num+2*colonne);
			
			argC.add(2);
			argL.add(2);
			break;
		case RIGHT:
			lst.add(num-1);
			lst.add(num-2);
			lst.add(num+colonne);
			lst.add(num+2*colonne);
			
			argC.add(-2);
			argL.add(2);
			break;
		case DOWN:
			lst.add(num-1);
			lst.add(num-2);
			lst.add(num-colonne);
			lst.add(num-2*colonne);
			
			argC.add(-2);
			argL.add(-2);
			break;
		case LEFT:
			lst.add(num+1);
			lst.add(num+2);
			lst.add(num-colonne);
			lst.add(num-2*colonne);
			
			argC.add(2);
			argL.add(-2);
			break;
		}
		
		if(!isInPlateau(num, argC, argL)) {
			return null;
		}
		
		return lst;
	}

	private List<Integer> casesW(int num, Sens sens) {
		List<Integer> lst = new ArrayList<Integer>();
		List<Integer> argC = new ArrayList<Integer>();
		List<Integer> argL = new ArrayList<Integer>();
		
		lst.add(num);
		switch(sens) {
		case UP:
			lst.add(num-colonne);
			lst.add(num-colonne-1);
			lst.add(num+1);
			lst.add(num+colonne+1);
			break;
		case RIGHT:
			lst.add(num-colonne+1);
			lst.add(num+1);
			lst.add(num+colonne);
			lst.add(num+colonne-1);
			break;
		case DOWN:
			lst.add(num-colonne-1);
			lst.add(num-1);
			lst.add(num+colonne);
			lst.add(num+colonne+1);
			break;
		case LEFT:
			lst.add(num+colonne-1);
			lst.add(num-1);
			lst.add(num-colonne);
			lst.add(num-colonne+1);
			break;
		}
		
		argC.add(1);
		argC.add(-1);
		argL.add(1);
		argL.add(-1);
		
		if(!isInPlateau(num, argC, argL)) {
			return null;
		}
		
		return lst;
	}

	private List<Integer> casesX(int num, Sens sens) {
		List<Integer> lst = new ArrayList<Integer>();
		List<Integer> argC = new ArrayList<Integer>();
		List<Integer> argL = new ArrayList<Integer>();
		
		switch(sens) {
		case UP:
			lst.add(num);
			lst.add(num-1);
			lst.add(num+1);
			lst.add(num-colonne);
			lst.add(num+colonne);
			break;
		case RIGHT:
		case DOWN:
		case LEFT:
			return null;
		}
		
		argC.add(1);
		argC.add(-1);
		argL.add(1);
		argL.add(-1);
		
		if(!isInPlateau(num, argC, argL)) {
			return null;
		}
		
		return lst;
	}

	private List<Integer> casesY(int num, Sens sens) {
		List<Integer> lst = new ArrayList<Integer>();
		List<Integer> argC = new ArrayList<Integer>();
		List<Integer> argL = new ArrayList<Integer>();
		
		lst.add(num);
		switch(sens) {
		case UP:
			lst.add(num-1);
			lst.add(num+1);
			lst.add(num+colonne);
			lst.add(num+2);
			
			argC.add(2);
			argC.add(-1);
			argL.add(1);
			break;
		case RIGHT:
			lst.add(num-1);
			lst.add(num-colonne);
			lst.add(num+colonne);
			lst.add(num+2*colonne);
			
			argC.add(-1);
			argL.add(2);
			argL.add(-1);
			break;
		case DOWN:
			lst.add(num-1);
			lst.add(num-2);
			lst.add(num-colonne);
			lst.add(num+1);
			
			argC.add(1);
			argC.add(-2);
			argL.add(-1);
			break;
		case LEFT:
			lst.add(num+1);
			lst.add(num+colonne);
			lst.add(num-colonne);
			lst.add(num-2*colonne);
			
			argC.add(1);
			argL.add(1);
			argL.add(-2);
			break;
		}
		
		if(!isInPlateau(num, argC, argL)) {
			return null;
		}
		
		return lst;
	}

	private List<Integer> casesZ(int num, Sens sens) {
		List<Integer> lst = new ArrayList<Integer>();
		List<Integer> argC = new ArrayList<Integer>();
		List<Integer> argL = new ArrayList<Integer>();
		
		lst.add(num);
		switch(sens) {
		case UP:
			lst.add(num-1);
			lst.add(num-colonne-1);
			lst.add(num+1);
			lst.add(num+colonne+1);
			break;
		case RIGHT:
			lst.add(num-colonne);
			lst.add(num-colonne+1);
			lst.add(num+colonne);
			lst.add(num+colonne-1);
			break;
		case DOWN:
		case LEFT:
			return null;
		}
		
		argC.add(1);
		argC.add(-1);
		argL.add(1);
		argL.add(-1);
		
		if(!isInPlateau(num, argC, argL)) {
			return null;
		}
		
		return lst;
	}
	
	private int getNumColonne(int num) {
		return num % colonne;
	}
	
	private int getNumLigne(int num) {
		return (num - getNumColonne(num)) / colonne;
	}
	
	private boolean isInPlateau(int num, List<Integer> argC, List<Integer> argL) {
		int x = getNumColonne(num);
		int y = getNumLigne(num);
		
		for(int i : argC) {
			if(x + i < 0 || x + i >= colonne) {
				return false;
			}
		}
		for(int i : argL) {
			if(y + i < 0 || y + i >= ligne) {
				return false;
			}
		}
		
		return true;
	}
}
